package com..cc.mc.road.core.domain.service.impl;

import com..cc.mc.road.core.application.dto.NodServRestDto;
import com..cc.mc.road.core.application.dto.ResponseData;
import com..cc.mc.road.core.domain.Component.McRoadNodServRestHandler;
import com..cc.mc.road.core.domain.model.McRoadExecute;
import com..cc.mc.road.core.domain.model.McRoadNodInfo;
import com..cc.mc.road.core.domain.service.McRoadNodSetService;
import com..cc.mc.road.core.domain.service.McRoadSimpleExecuteService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;


/**
 * @author 周丹涌/80374563
 * @ClassName: McRoadNodSetServiceImpl
 * @date 2018年9月18日 下午4:42:38
 */
@Service
public class McRoadSimpleExecuteServiceImpl implements McRoadSimpleExecuteService {

    private static final Logger LOGGER = LoggerFactory.getLogger(McRoadSimpleExecuteServiceImpl.class);

    private McRoadExecute mcRoadExecute;

    private final String ROAD_EXEC_CODE = "500";

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private McRoadNodSetService mcRoadNodSetService;

    @Autowired
    private McRoadNodServRestHandler restHandler;

    /**
     * 根据通路ID初始化聚合
     * @param radId
     */
    private void mcRoadSimpleExecuteInitialze(String radId, String crdOrdNbr) {
        this.mcRoadExecute = new McRoadExecute(radId, crdOrdNbr, ROAD_EXEC_CODE);
        this.mcRoadExecute.setMcRoadNodSet(mcRoadNodSetService.constructeMcRoadNodSet(radId));

    }


    /**
     * rest方式执行具体调用
     * @param radId
     * @param crdOrdNbr
     * @param item
     * @return ResponseData
     */
    public ResponseData execMcRoadNodeServiceWithRest(String radId, String crdOrdNbr, McRoadNodInfo item){
        NodServRestDto dto = new NodServRestDto(this.mcRoadExecute.getExecNo(),crdOrdNbr,
                item.getMethod(), item.getRedoMax(), item.getSrvNam(), item.getSrvUrl() );
        // 调用服务
        ResponseData responseData = restHandler.getNodeServiceExecuteResult(dto);
        // 验证返回值
        if (null != responseData && responseData.isSuccess()) {
            LOGGER.info("调用服务" + item.getSrvNam() + "(" + item.getSrvId() + ")成功");
            //TODO 记录调用日志
        } else {
            LOGGER.error("调用服务" + item.getSrvNam() + "(" + item.getSrvId() + ")失败");
            //TODO 记录调用日志
            throw new RuntimeException("调用服务" + item.getSrvNam() + "(" + item.getSrvId() + ")失败");
        }
        return responseData;

    }


    /**
     * 执行方式一：按通路节点的执行顺序获取节点服务集合，同步执行节点
     */
    @Override
    public List<McRoadExecute> execMcRoadNodeServicesProcess(String radId, String crdOrdNbr)  throws Exception{
        if(StringUtils.isEmpty(radId)){
            throw new NullPointerException("radId不能为空");
        }
        //1. 初始化本通路相关聚合信息
        this.mcRoadSimpleExecuteInitialze(radId, crdOrdNbr);
        LOGGER.info("制卡通路:"+radId+"初始化完成，本次执行编号:"+this.mcRoadExecute.getExecNo());


        //2. 对通路所含的节点服务集进行顺序处理
        mcRoadExecute.getMcRoadNodSet().getNodServicesList().forEach(item -> {
            // 调用服务
            ResponseData responseData = execMcRoadNodeServiceWithRest(radId, crdOrdNbr, item);
        });


        return null;
    }



    /**
    * 执行方式二：按通路节点的执行顺序获取节点服务集合, 异步执行各个节点服务
    */
    @Override
    public List<McRoadExecute> execMcRoadNodeServicesProcessAsync(String radId, String crdOrdNbr)  throws Exception{
        if(StringUtils.isEmpty(radId)){
            throw new NullPointerException("radId不能为空");
        }
        //1. 初始化本通路相关聚合信息
        this.mcRoadSimpleExecuteInitialze(radId, crdOrdNbr);
        LOGGER.info("制卡通路:"+radId+"初始化完成，本次执行编号:"+this.mcRoadExecute.getExecNo());


        //2. 对通路所含的节点服务集进行异步执行处理
        List<McRoadNodInfo>  nodInfolist = mcRoadExecute.getMcRoadNodSet().getNodServicesList();
        /**
         * 异步查询
         * 相比并行流的话CompletableFuture更有优势：可以对执行器配置，设置线程池大小
         */
        @SuppressWarnings("all")
        final Executor myExecutor = Executors.newFixedThreadPool(Math.min(nodInfolist.size(), 800), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                //使用守护线程保证不会阻止程序的关停
                t.setDaemon(true);
                return t;
            }
        });

        // 流式异步调用
        List<CompletableFuture<ResponseData>> execFuctures = nodInfolist.stream()
                .map(McRoadNodInfo -> CompletableFuture.supplyAsync(() -> execMcRoadNodeServiceWithRest(radId, crdOrdNbr, McRoadNodInfo), myExecutor))
                .collect(Collectors.toList());
        /**
         * 这里需要使用新的stream来等待所有的子线程执行完，
         *   因为：如果在一个stream中使用两个map, 考虑到流操作之间的延迟特性。如果你在单一的流水线中处理流，
         *   发向不同商家的请求只能以同步顺序的方式执行才会成功。因此每个创建CompletableFuture
         *   对象只能在前一个操作结束之后执行查询商家动作。
         */
        List<ResponseData> rzlist = execFuctures.stream().map(c -> c.join()).collect(Collectors.toList());


        rzlist.forEach(ResponseData::printResponseContent);

        return null;
    }



}